package com.hqd.ch03.v50.boot.context.properties;

import com.hqd.ch03.v50.beans.factory.config.BeanDefinition;
import com.hqd.ch03.v50.beans.factory.support.BeanDefinitionBuilder;
import com.hqd.ch03.v50.boot.context.properties.source.ConfigurationProperty;
import com.hqd.ch03.v50.boot.context.properties.source.ConfigurationPropertyName;
import com.hqd.ch03.v50.context.ApplicationContext;
import com.hqd.ch03.v50.registry.BeanDefinitionRegistry;

import java.util.Collections;
import java.util.LinkedHashMap;
import java.util.Map;

public class BoundConfigurationProperties {

    /**
     * The bean name that this class is registered with.
     */
    private static final String BEAN_NAME = BoundConfigurationProperties.class.getName();
    private Map<ConfigurationPropertyName, ConfigurationProperty> properties = new LinkedHashMap<>();

    /**
     * Return the {@link BoundConfigurationProperties} from the given
     * {@link ApplicationContext} if it is available.
     *
     * @param context the context to search
     * @return a {@link BoundConfigurationProperties} or {@code null}
     */
    public static BoundConfigurationProperties get(ApplicationContext context) {
        if (!context.containsBeanDefinition(BEAN_NAME)) {
            return null;
        }
        return context.getBean(BEAN_NAME, BoundConfigurationProperties.class);
    }

    static void register(BeanDefinitionRegistry registry) {
        if (!registry.containsBeanDefinition(BEAN_NAME)) {
            BeanDefinition definition = BeanDefinitionBuilder
                    .genericBeanDefinition(BoundConfigurationProperties.class, BoundConfigurationProperties::new)
                    .getBeanDefinition();
            definition.setRole(BeanDefinition.ROLE_INFRASTRUCTURE);
            registry.registerBeanDefinition(BEAN_NAME, definition);
        }
    }

    void add(ConfigurationProperty configurationProperty) {
        this.properties.put(configurationProperty.getName(), configurationProperty);
    }

    /**
     * Get the configuration property bound to the given name.
     *
     * @param name the property name
     * @return the bound property or {@code null}
     */
    public ConfigurationProperty get(ConfigurationPropertyName name) {
        return this.properties.get(name);
    }

    /**
     * Get all bound properties.
     *
     * @return a map of all bound properties
     */
    public Map<ConfigurationPropertyName, ConfigurationProperty> getAll() {
        return Collections.unmodifiableMap(this.properties);
    }

}

